package poker;

import java.util.Vector;

import poker.beans.*;
import poker.beans.PlayerBean.Action;
import poker.UI;
import poker.Evaluator;
import poker.Chat;

import org.directwebremoting.ScriptSessions;

public class Engine{	
	
	public Engine(){
	}
	
	private GameBean game = null;
	
		
	public void startGame(){

		game = new GameBean();		
		
		game.initGame();
		game.initPlayers();		
		
		round();
	}
	
	public void nextRound(){
		if( game.getPlayers().size() > 1 ){
			getReadyForNextRound();
			round();
		}
		else{
			declareWinner();
			endGame();
		}
	}

	private void round() {
		if(game.isWinnerDeclared()) return;
		switch(game.getStage()){
		case 0:
			game.makeDeck();
			game.shuffleDeck();
			game.dealCards();
			sleep(1000);
			UI.showPlayerCards(game.getPlayer(0));
			UI.isDealer(game.getDealerId());			
			game.correctNumOfActivePlayers();
			game.setBlinds();
			game.setBetMultiplier(1);
			playerAction();
			break;
		case 1:
			game.burn();
			playStage();
			break;
		case 2:
			game.setBetMultiplier(2);
			playStage();
			break;
		case 3:
			playStage();
			break;
		case 4:
			showdown();
			UI.displayContinueButton();
			break;
		default:
			//logger("Error terror in code");
			break;
		}	
	}
		
	private void endGame() {
		game = null;
		// TODO logger		
	}

	private void declareWinner() {
		game.setWinnerDeclared(true);
		//TODO logger
	}

	private void getReadyForNextRound() {
ScriptSessions.addFunctionCall("alert","next round");
		game.setStage(0);
		UI.removeDealer(game.getDealerId());
		game.incrementDealer();
		game.setPot(0);
		eliminateBrokePlayers();
		if(game.getDealer() >= game.getPlayers().size()){
			game.setDealer(0);
			game.increaseBlinds();
		}
		game.setCommunityCardIndex(0);
		for(PlayerBean player : game.getPlayers()){
			player.setInGame(true);
			player.setCards(new Vector<CardBean>(2));
			player.setStrongestHand(0);
		}
		//TODO logger
		game.setCommunityCards(new Vector<CardBean>(5));		
		UI.resetHtmlForRound(game);
		getReadyForNextStage();
		
	}

	private void getReadyForNextStage() {
		UI.resetHtmlForStage(game);
		UI.updatePotAmount(game.getPot());
		
		for(PlayerBean player : game.getPlayers()){
			player.setHasActed(false);
			player.setBetSize(0);
			if(player.getAction() == Action.FOLD) player.setAction(Action.NOACTION);
		}
		
		game.setMaxBet(0);
		game.setTimesRaised(0);
		round();		
	}

	

	private void eliminateBrokePlayers() {
		int i = 0;
		for(PlayerBean player : game.getPlayers()){
			i++;
			if(player.getMoney() < game.getSmallBlind() * 2){
				UI.hidePlayer(player);
				if(game.getPlayers().indexOf(player) < game.getDealer()) game.decrementDealer();
				game.removePlayer(player);				
			}			
		}		
		//TODO logger		
	}

	private void showdown() {
		if(game.getPlayersInGame() == 1){
			for(PlayerBean player : game.getPlayers()){
				if(player.isInGame()){
					player.addWinnings(game.getPot());
					break;
				}
			}
		}
		
		else{
			int bestHandStrength = 0;
			Vector<PlayerBean> winners = new Vector<PlayerBean>();

			for(PlayerBean player : game.getPlayers()){
				if(player.isInGame()){
					UI.showPlayerCards(player);
					Evaluator.findBestHand(game, player);
					if(player.getStrongestHand() > bestHandStrength){
						bestHandStrength = player.getStrongestHand();
						winners.clear();
						winners.add(player);
					}
					else if(player.getStrongestHand() == bestHandStrength){
						winners.add(player);
					}
				}
			}
			for(PlayerBean player : winners){
				player.setMoney(game.getPot() / winners.size());
				//TODO logger
			}
			
			for(PlayerBean player : game.getPlayers()){
				UI.updatePlayerData(player);
			}
			game.setPot(0);
			UI.updatePotAmount(0);
		}		
	}	

	private void playStage() {
		if( game.getPlayersInGame() == 1){
			game.setStage(4);
			round();
		}
		else{
			int cardsToDeal = 1;
			if( game.getStage() == 1 ) cardsToDeal = 3;
			for (int i = 0; i < cardsToDeal; i++){
				game.addCommunityCard();
				UI.writeCommunityCardToHtml(game);
				game.burn();				
				game.incrementCommunityCardIndex();
			}
			try{
				game.setActivePlayerIndex((game.getDealer() + 1) % game.getPlayers().size());
			}
			catch(Exception e){
				ScriptSessions.addFunctionCall("alert", "Error at: playStage");
			}
			game.correctActivePlayerIndex();		
			playerAction();
		}		
	}

	private void playerAction() {
		UI.isActivePlayer(game.getActivePlayer());
		if( game.getPlayersInGame() == 1 ){
			game.setStage(4);
			round();
			}
		else if( game.ready() ) {
			game.incrementStage();
			getReadyForNextStage();
		}
		else if (game.getActivePlayer().isHuman()){
			humanAction();		
		}	
		else {
			botAction();
		}		
	}	
	
	private void humanAction() {
//ScriptSessions.addFunctionCall("alert","human action");
		UI.displayButtons(game);
		ScriptSessions.addFunctionCall("humanAction");
		sleep(100);
	}
	
	public void humanFold(){
		PlayerBean player = game.getActivePlayer();
		player.makeAction(Action.FOLD, game);
		handOverTurn( player );
	}
	
	public void humanCall(){
		PlayerBean player = game.getActivePlayer();
		player.makeAction(Action.CALL, game);
		handOverTurn( player );
	}
	
	public void humanRaise(){
		PlayerBean player = game.getActivePlayer();
		player.makeAction(Action.RAISE, game);
		handOverTurn( player );
	}
	
	public void humanCheck(){
		PlayerBean player = game.getActivePlayer();
		player.makeAction(Action.FOLD, game);
		handOverTurn( player );
	}

	private void botAction() {
		sleep(1500);
		PlayerBean player = game.getActivePlayer();
		PlayerBean.Action a = player.generateAction(game);
		player.makeAction(a, game);
		
		handOverTurn( player );				
	}

	private void handOverTurn(PlayerBean player) {
		UI.updatePlayerData( player );
		game.incrementActivePlayer();
		game.correctActivePlayerIndex();		
		playerAction();
	}
	
	public static void sleep(int n){
		try {
			Thread.sleep(n);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
}
